﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.ComponentModel;
using System.Reflection;
using Microsoft.Forums.MicrosoftForumsProvider;
using Microsoft.Forums.SyncService;
using Microsoft.Forums.OfflineStore;
using System.Collections.ObjectModel;
using Microsoft.Forums.ForumsClient.Models;
using Microsoft.Forums.ForumsProvider;
using System.Data.Linq;
using System.Data.SqlClient;
using System.Data.SqlServerCe;

namespace Microsoft.Forums.ForumsClient.Models
{
    public class AppDataModel : BindableObject
    {
        #region Static Methods, Properties and Members

        static AppDataModel dataModel;

        /// <summary>
        /// Initialize the data model
        /// </summary>
        public static void Initialize()
        {
            SetupAppDataLocation();
            string dbFileName = Properties.Settings.Default.CurrentDatabaseFileName;
            
            dataModel = new AppDataModel(dbFileName);
        }

        /// <summary>
        /// Initialize the data model with interfaces
        /// This is used for test classes to be able to sub stub implementations
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="syncManager">The sync manager.</param>
        /// <param name="networkConnection">The network connection.</param>
        /// <param name="databaseFileName">Name of the database file.</param>
        public static void Initialize(
            IForumServiceProvider provider,
            SynchronizationManager syncManager,
            IConnection networkConnection,
            string databaseFileName,
            SubscribedForumsCollection forums)
        {
            dataModel = new AppDataModel(provider, syncManager, networkConnection, databaseFileName, forums);
        }

        /// <summary>
        ///  Sets up the app data location for the database file
        /// </summary>
        private static void SetupAppDataLocation()
        {
            // First time the CurrentDatabaseFileName would be empty.
            if (string.IsNullOrEmpty(Properties.Settings.Default.CurrentDatabaseFileName) == true)
            {
                Properties.Settings.Default.CurrentDatabaseFileName = FindOrCreateLocalAppDataFolder() + Properties.Settings.Default.DefaultDatabaseFileName;
            }
            else
            {
                System.IO.FileInfo fileInfo = new System.IO.FileInfo(Properties.Settings.Default.CurrentDatabaseFileName);

                // Does the file extension needs to be checked ?

                if (fileInfo.Directory.Exists == false)
                {
                    FindOrCreateDataFolder(fileInfo.Directory.FullName);
                }
            }
        }

        /// <summary>
        ///  Get the user local app data folder and format the path string to the App data Folder with the application name and version
        ///  If the folder doesn't exist create it.
        /// </summary>
        /// <returns>Path of the folder</returns>
        private static string FindOrCreateLocalAppDataFolder()
        {
            string path = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
            path = path + "\\Microsoft Forums Client\\" + Assembly.GetExecutingAssembly().GetName().Version + "\\";
            return FindOrCreateDataFolder(path);
        }

        /// <summary>
        ///  Create the Data Folder
        /// </summary>
        /// <param name="path">Path of the folder</param>
        /// <returns>path to the folder</returns>
        private static string FindOrCreateDataFolder(string path)
        {
            if (string.IsNullOrEmpty(path) == true)
            {
                throw new ArgumentNullException("path");
            }

            if (System.IO.Directory.Exists(path) == false)
            {
                // At present not catching any exception intentionally as there is nothing
                // we can do to handle it.
                System.IO.Directory.CreateDirectory(path);
            }
            return path;
        }


        /// <summary>
        /// The instance of the data model
        /// </summary>
        public static AppDataModel Instance
        {
            get
            {
                return AppDataModel.dataModel;
            }
        }

        #endregion


        #region Private Members

        /// <summary>
        /// The selected Post
        /// </summary>
        PostViewModel selectedPost;

        /// <summary>
        /// The selected folder
        /// </summary>
        IFolderViewModel selectedFolder;

        #endregion

        #region Public Properties
        /// <summary>
        /// A connection pool to get a connection object per thread
        /// </summary>
        public IConnectionPool ConnectionPool
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the synchronization manager.
        /// </summary>
        /// <value>The synchronization manager.</value>
        public SynchronizationManager SynchronizationManager
        {
            get;
            private set;
        }

        /// <summary>
        ///  Forum Service Provider
        /// </summary>
        public IForumServiceProvider ServiceProvider
        {
            get;
            set;
        }

        /// <summary>
        ///  Network details 
        /// </summary>
        public IConnection Network
        {
            get;
            private set;
        }

        /// <summary>
        ///  Current Theme Index of the app.
        /// </summary>
        public int ThemeIndex
        {
            get;
            set;
        }

        /// <summary>
        ///  Current Logged In user information.
        /// </summary>
        public UserLogOnInformation CurrentUser
        {
            get;
            set;
        }

        /// <summary>
        ///  Local Data Store for this client app.
        /// </summary>
        public ForumsOfflineStore LocalDataStore
        {
            get
            {
                return new ForumsOfflineStore(ConnectionPool.Connection);
            }
        }

        /// <summary>
        /// Gets a data store with its data load options set so that
        /// Every post also as its PostPropertySet and CreatedBy user loaded eagerly
        /// and every forum has its ForumPropertySet loaded eagerly
        /// and which has object tracking disabled.  This is meant for reads only
        /// and should not be used for writes to the database
        /// </summary>
        public ForumsOfflineStore ReadOnlyDataContext
        {
            get
            {
                var store = this.LocalDataStore;
                DataLoadOptions dlo = new DataLoadOptions();
                // Ensure the foreign key objects get loaded
                dlo.LoadWith<Post>(p => p.PostPropertySet);
                dlo.LoadWith<Post>(p => p.CreatedBy);
                dlo.LoadWith<Thread>(t => t.ThreadPropertySet);
                dlo.LoadWith<Forum>(f => f.ForumPropertySet);
                store.LoadOptions = dlo;
                store.ObjectTrackingEnabled = false;
                return store;
            }
        }


        /// <summary>
        ///  Current user Subscribed Forums.
        /// </summary>
        public SubscribedForumsCollection SubscribedForums
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets my threads folder.
        /// </summary>
        /// <value>My threads folder.</value>
        public MyThreadsFolderViewModel MyThreadsFolder
        {
            get;
            set;
        }


        /// <summary>
        /// The selected Forum
        /// </summary>
        public IFolderViewModel SelectedFolder
        {
            get
            {
                return selectedFolder;
            }
            set
            {
                if (!Object.ReferenceEquals(selectedFolder, value))
                {
                    selectedFolder = value;
                    OnPropertyChanged("SelectedFolder");
                }

            }
        }

        /// <summary>
        /// The selected Post
        /// </summary>
        public PostViewModel SelectedPost
        {
            get
            {
                return selectedPost;
            }
            set
            {
                if (!Object.ReferenceEquals(selectedPost, value))
                {
                    selectedPost = value;
                    OnPropertyChanged("SelectedPost");
                }

            }
        }

        /// <summary>
        ///  Connection string to the local store in use.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object)")]
        public string DatabaseConnectionString
        {
            get
            {
                return string.Format("Data Source={0}", DatabaseFileName);
            }
        }

        /// <summary>
        /// The name of the database file
        /// </summary>
        public string DatabaseFileName
        {
            get;
            private set;
        }

        #endregion


        /// <summary>
        /// Holds all the application level data
        /// </summary>
        /// <param name="databaseFileName">Name of the database file.</param>
        public AppDataModel(string databaseFileName)
        {
            DatabaseFileName = databaseFileName;

            ConnectionPool = new ConnectionPool(DatabaseConnectionString);
            CurrentUser = new UserLogOnInformation();

            SubscribedForums = new SubscribedForumsCollection();
            Network = new NetworkConnection();
            ServiceProvider = new MicrosoftForumsServiceProvider();
            SynchronizationManager = new SynchronizationManager();
            MyThreadsFolder = new MyThreadsFolderViewModel(CurrentUser);

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AppDataModel"/> class.
        /// This is used for test cases to be able to inject stub implementations of these interfaces
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <param name="syncManager">The sync manager.</param>
        /// <param name="networkConnection">The network connection.</param>
        /// <param name="databaseFileName">Name of the database file.</param>
        public AppDataModel(
            IForumServiceProvider provider,
            SynchronizationManager syncManager, 
            IConnection networkConnection,
            string databaseFileName,
            SubscribedForumsCollection forums)
        {
            ServiceProvider = provider;
            SynchronizationManager = syncManager;
            Network = networkConnection;
            DatabaseFileName = databaseFileName;
            SubscribedForums = forums;

            ConnectionPool = new ConnectionPool(DatabaseConnectionString);
            CurrentUser = new UserLogOnInformation();
            MyThreadsFolder = new MyThreadsFolderViewModel(CurrentUser);
        }

    }
}
